#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h> 
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/i2c.h>
#include <linux/ioctl.h>

#define DEV_NAME "MyOLED"
#define DEV_NUM  1

#define REG_CMD  0x00
#define REG_DATA 0x40

struct OLED_dev_t
{
    char *name;                     //设备名
    int num;                    //设备数量
    dev_t OLED_devid;                //OLED设备号
    struct cdev OLED_cdev;	        //cdev结构体	    
	struct class *OLED_class;	    //OLED类
	struct device *OLED_device;	    //OLED设备
    struct file_operations fops;     //操作函数
    struct i2c_client *client;          //iic设备
};

struct OLED_GRAM_t
{
    u8 GRAM[8][128];
};

struct OLED_GRAM_t OLED_GRAM;
struct OLED_dev_t OLED_dev;

#define CMD_IOC_MAGIC	'a'
#define CMD_CLEAR		_IOW(CMD_IOC_MAGIC, 0,int)
#define CMD_REFRESH		_IOW(CMD_IOC_MAGIC, 1, struct OLED_GRAM_t)

static int i2c_readBytes(struct i2c_client *client,u8 reg, void *val, int len)
{
	int ret;
	struct i2c_msg msg[2];

	/* msg[0]为发送要读取的首地址 */
	msg[0].addr = client->addr;			/* OLED地址 */
	msg[0].flags = 0;					/* 标记为发送数据 */
	msg[0].buf = &reg;					/* 读取的首地址 */
	msg[0].len = 1;						/* reg长度*/

	/* msg[1]读取数据 */
	msg[1].addr = client->addr;			/* OLED地址 */
	msg[1].flags = I2C_M_RD;			/* 标记为读取数据*/
	msg[1].buf = val;					/* 读取数据缓冲区 */
	msg[1].len = len;					/* 要读取的数据长度*/

	ret = i2c_transfer(client->adapter, msg, 2);
	if(ret == 2) {
		ret = 0;
	} else {
		printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
		ret = -EREMOTEIO;
	}
	return ret;
}
static int i2c_writeBytes(struct i2c_client *client,u8 reg, u8 *val, int len)
{
    u8 b[256];
	struct i2c_msg msg;
	
	b[0] = reg;					/* 寄存器首地址 */
	memcpy(&b[1],val,len);		/* 将要写入的数据拷贝到数组b里面 */
		
	msg.addr = client->addr;	/* OLED地址 */
	msg.flags = 0;				/* 标记为写数据 */

	msg.buf = b;				/* 要写入的数据缓冲区 */
	msg.len = len + 1;			/* 要写入的数据长度 */

	return i2c_transfer(client->adapter, &msg, 1);
}
static int i2c_readOneByte(struct i2c_client *client,u8 reg, u8 *data)
{
	i2c_readBytes(client, reg, data, 1);
    return 0;
}
static int i2c_writeOneByte(struct i2c_client *client,u8 reg, u8 data)
{
    u8 buf = 0;
	buf = data;
	i2c_writeBytes(client, reg, &buf, 1);
    return 0;
}
static int OLED_DrawPoint(int x,int y)
{
    //x:0~127
    //y:0~7
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0xB0+y);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,(x&0xF0)>>4|0x10);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,(x&0x0F)|0x00);
    i2c_writeOneByte(OLED_dev.client,REG_DATA,0x01);
    return 0;
}
static void OLED_FullScreen(int color)
{
    int i,j;
    for(i=0;i<8;i++)
    {
        i2c_writeOneByte(OLED_dev.client,REG_CMD,0xB0+i);
        i2c_writeOneByte(OLED_dev.client,REG_CMD,0x00);
        i2c_writeOneByte(OLED_dev.client,REG_CMD,0x10);
        for(j=0;j<128;j++)
        {
            i2c_writeOneByte(OLED_dev.client,REG_DATA,color);
        }
    }
}
static void OLED_Init(void)
{
    mdelay(500);														/* OLED复位最少500ms 	*/
	i2c_writeOneByte(OLED_dev.client,REG_CMD,0xAE);     //命令寄存器地址  寄存器数据
	i2c_writeOneByte(OLED_dev.client,REG_CMD,0x00);
	i2c_writeOneByte(OLED_dev.client,REG_CMD,0x10);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0x40);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0x81);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0xCF);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0xA1);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0xC8);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0xA6);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0xA8);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0x3F);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0xD3);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0x00);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0xD5);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0x80);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0xD9);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0xF1);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0xDA);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0x12);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0xDB);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0x40);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0x20);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0x02);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0x8D);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0x14);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0xA4);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0xA6);
    i2c_writeOneByte(OLED_dev.client,REG_CMD,0xAF);
    printk("The OLED is Init!\r\n");
}
static int OLED_open(struct inode *inode, struct file *filp)
{
    /* 初始化OLED */
    // OLED_Init();
    // OLED_FullScreen(0xFF);
    // OLED_DrawPoint(64,4);
	return 0;
}
static ssize_t OLED_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
    
	return 0;
}
static ssize_t OLED_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	unsigned char databuf[2];
    unsigned char reg;   //寄存器地址
    char msg;            //寄存器数据
	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}
    reg = databuf[0];
    msg = databuf[1];
    i2c_writeOneByte(OLED_dev.client,reg,msg);
    return 0;
}
static int OLED_release(struct inode *inode, struct file *filp)
{
    return 0;
}
static long OLED_ioctl(struct file *fd, unsigned int request, unsigned long args)
{
    int ret = 0;
    int color;
    switch(request)
    {
        case CMD_CLEAR:
            // ret = copy_from_user(color,(int __user *)args,sizeof(color));
            color = (int )args;
            OLED_FullScreen(color);
            ret = 1;
            break;
        case CMD_REFRESH:
            ret = copy_from_user(&OLED_GRAM,(struct OLED_GRAM_t *)args,sizeof(OLED_GRAM));
            ret = 2;
            break;
        default:
            return -ENOTTY;
    }
    return ret;
}
static int OLED_probe(struct i2c_client *client,const struct i2c_device_id *device_id)
{
    /*
    .定义操作函数
    .定义设备结构体
    .申请设备号
    .绑定字符设备和操作函数
    .绑定字符设备和设备号
    .创建类,设备
    */

    //IIC设备结构体
    OLED_dev.client = client;

    //填充fops结构体
    OLED_dev.fops.owner = THIS_MODULE;
    OLED_dev.fops.open = OLED_open;
    OLED_dev.fops.read = OLED_read;
    OLED_dev.fops.write = OLED_write;
    OLED_dev.fops.unlocked_ioctl = OLED_ioctl;
    OLED_dev.fops.release = OLED_release;
    
    //申请设备号
    OLED_dev.name = DEV_NAME;
    OLED_dev.num = DEV_NUM;
    alloc_chrdev_region(&OLED_dev.OLED_devid,0,OLED_dev.num,OLED_dev.name);

    //绑定cdev结构体和文件操作集合
    OLED_dev.OLED_cdev.owner = THIS_MODULE;
    cdev_init(&OLED_dev.OLED_cdev,&OLED_dev.fops);

    //绑定cdev结构体和设备号
    cdev_add(&OLED_dev.OLED_cdev,OLED_dev.OLED_devid,OLED_dev.num);

    //创建类和设备
    OLED_dev.OLED_class = class_create(THIS_MODULE,OLED_dev.name);        //创建类
    OLED_dev.OLED_device = device_create(OLED_dev.OLED_class, NULL, OLED_dev.OLED_devid, NULL, OLED_dev.name);//创建设备

    printk("Adder:%02x\r\n",client->addr);
    OLED_Init();
    OLED_FullScreen(0x00);
    OLED_DrawPoint(64,4);
    return 0;
}
static int OLED_remove(struct i2c_client *client)
{
    return 0;
}
/* 传统匹配方式ID列表 */
static const struct i2c_device_id OLED_id[] = {
	{"alientek,OLED", 0},  
	{}
};
static struct of_device_id IIC_of_match[] = 
{
    {.compatible = "MyOLEDDriver"},
    { /* Sentinel */ }
};
static struct i2c_driver OLED_driver = {
    .driver		= {
        .owner = THIS_MODULE,
		.name	= "MyOLEDDriver",			/* 驱动名字，用于和设备匹配 */
		.of_match_table	= IIC_of_match,     /* 设备树匹配表，用于和设备匹配*/
	},
    .id_table = OLED_id,
	.probe		= OLED_probe,
	.remove		= OLED_remove,
};
static int __init Init(void)
{
    int ret = 0;
    printk("Begin Init...\r\n");
	ret = i2c_add_driver(&OLED_driver);
    // printk("ret:%d\r\n",ret);
	return ret;
}
static void __exit Exit(void)
{
    i2c_del_driver(&OLED_driver);
}

module_init(Init);
module_exit(Exit);
MODULE_AUTHOR("ZiXie");
MODULE_LICENSE("GPL");